package com.example.springbootdemo.service;

import com.example.springbootdemo.dto.GeoJsonDto;
import org.geotools.data.*;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.FeatureCollection;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.referencing.CRS;
import org.opengis.feature.simple.SimpleFeature;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geometry.jts.JTS;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class GeoJsonService {


    public static void writeFeaturesToShapefile(ShapefileDataStore dataStore, SimpleFeatureCollection featureCollection) throws Exception {

        Transaction transaction = new DefaultTransaction("create");
        // 使用 FeatureWriter 代替 SimpleFeatureWriter
        FeatureWriter<SimpleFeatureType, SimpleFeature> writer = dataStore.getFeatureWriterAppend(dataStore.getTypeNames()[0], transaction);

        SimpleFeatureIterator iterator = featureCollection.features();
        try {
            while (iterator.hasNext()) {
                SimpleFeature feature = iterator.next();//name type geometry
                System.out.println(feature.getID());
                SimpleFeature newFeature = writer.next();//the_geom name type
                newFeature.setAttributes(feature.getAttributes());
                writer.write();
            }
        } finally {
            iterator.close();
        }
        writer.close();
        transaction.commit();
        transaction.close();
        dataStore.dispose();

        System.out.println("Shapefile 创建成功！");
    }

    /**
     * 几何字段映射报错
     *
     * @param dataStore
     * @param featureCollection
     * @throws Exception
     */
    public static void writeFeaturesToShapefile1(ShapefileDataStore dataStore, SimpleFeatureCollection featureCollection) throws Exception {

        SimpleFeatureSource featureSource = dataStore.getFeatureSource(dataStore.getTypeNames()[0]);
        if (featureSource instanceof SimpleFeatureStore featureStore) {
//                featureStore.setTransaction(new DefaultTransaction("UTF-8")); // 设置事务编码为UTF-8
            featureStore.addFeatures(featureCollection);
        } else {
            throw new IllegalStateException("数据存储不支持写入操作");
        }
    }



    private static SimpleFeatureType createTargetFeatureType(SimpleFeature firstFeature, Geometry geometry) throws Exception {
        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.setName("Shapefile");
        typeBuilder.setCRS(CRS.decode("EPSG:4326"));

        // 添加几何字段（使用第一个要素的类型）
        typeBuilder.add("geometry", geometry.getClass());

        // 添加属性字段
        firstFeature.getFeatureType().getAttributeDescriptors().stream().filter(desc -> !"geometry".equals(desc.getLocalName())).forEach(desc -> typeBuilder.add(desc.getLocalName(), desc.getType().getBinding()));

        return typeBuilder.buildFeatureType();
    }


    //----------------------------------------------------------
    // 核心方法：重映射FeatureCollection
    //----------------------------------------------------------
    private static SimpleFeatureCollection remapFeatureCollection(SimpleFeatureCollection inputCollection, SimpleFeatureType targetType, GeometryDescriptor originalGeometryDesc) {
        ListFeatureCollection outputCollection = new ListFeatureCollection(targetType);
        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(targetType);

        try (SimpleFeatureIterator features = inputCollection.features()) {
            while (features.hasNext()) {
                SimpleFeature sourceFeature = features.next();

                // 按目标类型字段顺序自动映射属性
                for (AttributeDescriptor targetAttr : targetType.getAttributeDescriptors()) {
                    String attrName = targetAttr.getLocalName();
                    Object value;

                    if (attrName.equals(targetType.getGeometryDescriptor().getLocalName())) {
                        // 映射几何字段
                        value = sourceFeature.getAttribute(originalGeometryDesc.getName());
                    } else {
                        // 映射其他字段（按名称匹配）
                        value = sourceFeature.getAttribute(attrName);
                    }

                    featureBuilder.set(attrName, value);
                }

                // 构建新Feature并添加到集合
                SimpleFeature adjustedFeature = featureBuilder.buildFeature(null);
                outputCollection.add(adjustedFeature);
            }
        }

        return outputCollection;
    }


    public byte[] convertGeoJsonToShp(GeoJsonDto geoJsonDto) {
        try {

            String epsg="EPSG:4326";
            String charset="UTF-8";
            charset = geoJsonDto.getCharset();
            epsg = geoJsonDto.getEpsg();

            // 1.为每个请求生成唯一的临时目录
            String uniqueDirName = "gisdata/geotools/createshp2/" ;//+ UUID.randomUUID().toString();
            Path tempDir = Paths.get(uniqueDirName);
            // 删除临时目录
            if (Files.exists(tempDir)) {
                Files.walk(tempDir).sorted((p1, p2) -> -p1.compareTo(p2)).forEach(path -> {
                    try {
                        Files.delete(path);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            }
            // 创建临时目录
            if (!Files.exists(tempDir)) {
                Files.createDirectories(tempDir);
            }
            // 2.解析GeoJSON，确保使用UTF-8编码
            FeatureJSON featureJSON = new FeatureJSON();
            String geoJsonstr = "{\n" +
                    "    \"type\": \"FeatureCollection\",\n" +
                    "    \"features\": [\n" +
                    "   \n" +
                    "      {\n" +
                    "        \"type\": \"Feature\",\n" +
                    "        \"geometry\": {\n" +
                    "          \"type\": \"Polygon\",\n" +
                    "          \"coordinates\": [\n" +
                    "            [\n" +
                    "              [116.397428, 39.90923],\n" +
                    "              [116.407428, 39.90923],\n" +
                    "              [116.407428, 39.91923],\n" +
                    "              [116.397428, 39.91923],\n" +
                    "              [116.397428, 39.90923]\n" +
                    "            ]\n" +
                    "          ]\n" +
                    "        },\n" +
                    "        \"properties\": {\n" +
                    "          \"name\": \"11\",\n" +
                    "          \"type\": \"22区域\"\n" +
                    "        }\n" +
                    "      }\n" +
                    "    ]\n" +
                    "  }";

            FeatureCollection<SimpleFeatureType, SimpleFeature> rawCollection = featureJSON.readFeatureCollection(geoJsonstr);
            SimpleFeatureCollection featureCollection = DataUtilities.simple(rawCollection);//name type geometry
            // 定义FeatureType名称
            String featureTypeName = "output";
            // 定义shpfile名称
            String shpfieName = "output";

            // 动态获取几何字段
            SimpleFeatureType originalType = featureCollection.getSchema();
            if(originalType == null){
                throw new IllegalArgumentException("输入数据未包含要素!");
            }
            GeometryDescriptor originalGeometryDesc = originalType.getGeometryDescriptor();
            if (originalGeometryDesc == null) {
                throw new IllegalArgumentException("输入数据未包含几何字段!");
            }
            //   Class<? extends Geometry> geometryType =originalGeometryDesc.getType().getBinding();

            // 获取第一个要素
            SimpleFeature firstFeature = featureCollection.features().next();
            // 获取第一个要素的几何类型

            Class<? extends Geometry> geometryType = (Class<? extends Geometry>) firstFeature.getDefaultGeometry().getClass();


            // 3 创建FeatureType:
            SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
            typeBuilder.setName(featureTypeName);
            typeBuilder.setCRS(CRS.decode(epsg, true)); // 确保坐标系正确
            typeBuilder.add(originalGeometryDesc.getLocalName(), geometryType);
            //                typeBuilder.add("name", String.class); // 添加属性字段
            //                typeBuilder.add("type", String.class); // 添加属性字段
            // 添加属性字段
            firstFeature.getFeatureType().getAttributeDescriptors().stream().filter(desc -> !originalGeometryDesc.getLocalName().equals(desc.getLocalName())).forEach(desc -> typeBuilder.add(desc.getLocalName(), desc.getType().getBinding()));
            // 显式设置默认几何字段（可选但建议）
            typeBuilder.setDefaultGeometry(originalGeometryDesc.getLocalName());
            SimpleFeatureType adjustedType = typeBuilder.buildFeatureType();// geometry name type


            //4.创建shp文件
            ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
            File shpFile = new File(tempDir.toFile(), shpfieName+".shp");
            Map<String, Serializable> params = new HashMap<>();
            params.put(ShapefileDataStoreFactory.URLP.key, shpFile.toURI().toURL());
            params.put(ShapefileDataStoreFactory.DBFCHARSET.key, "UTF8");
            params.put(ShapefileDataStoreFactory.CREATE_SPATIAL_INDEX.key, true);
            ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);



            dataStore.setCharset(Charset.forName("UTF-8"));
            // 设置要素类型（定义字段结构），先删除再创建shp文件：shp dbf prj shx ;
            dataStore.createSchema(adjustedType);
            // 创建CPG文件指定编码
            Path cpgPath = Paths.get(shpFile.getAbsolutePath().replace(".shp", ".cpg"));
            Files.write(cpgPath, charset.getBytes(StandardCharsets.UTF_8));
            // 添加调试日志
            System.out.println("Created feature type: " + featureTypeName);
            System.out.println("Available types: " + dataStore.getNames());
            // 确保FeatureType已经创建
            int maxTries = 2;
            int tries = 0;
            while (!dataStore.getNames().contains(featureTypeName) && tries < maxTries) {
                System.out.println("Waiting for FeatureType to be created...");
                Thread.sleep(1000); // 等待1秒
                tries++;
            }



            // 6. 创建重映射后的FeatureCollection ：解决字段映射失败导致的导入失败问题
            SimpleFeatureCollection adjustedCollection = remapFeatureCollection(featureCollection, adjustedType, originalGeometryDesc);
            // 7写入数据
            writeFeaturesToShapefile(dataStore, adjustedCollection);


            // 生成ZIP文件
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try (ZipOutputStream zipOut = new ZipOutputStream(baos)) {
                for (File file : tempDir.toFile().listFiles()) {
                    zipOut.putNextEntry(new ZipEntry(file.getName()));
                    Files.copy(file.toPath(), zipOut);
                    zipOut.closeEntry();
                }
            }

            // 删除临时目录
//            Files.walk(tempDir)
//                    .sorted((p1, p2) -> -p1.compareTo(p2))
//                    .forEach(path -> {
//                        try {
//                            Files.delete(path);
//                        } catch (IOException e) {
//                            e.printStackTrace();
//                        }
//                    });

            return baos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException("Failed to convert GeoJSON to SHP", e);
        }

    }
}